package net.cyndeline.GameMap.dataStructure
import net.cyndeline.MovementDirection
import com.escalatesoft.subcut.inject.BindingModule
import com.escalatesoft.subcut.inject.Injectable
import net.cyndeline.GameMap.GridNodeExaminer
import net.cyndeline.GameMap.Tile
import net.cyndeline.GameMap.dataStructure.examinators.SquareNodeExaminer

/**
 * Represents a single node on a grid.
 */
abstract class Node
(implicit val bindingModule: BindingModule) extends Tile with Injectable {
  private var xpos: Int = 0
  private var ypos: Int = 0
  
  /* Keeps coordinate from being set more than once. */
  private var xSet = false
  private var ySet = false
  
  /* Compares this node with others. */
  private val nodeExaminer = injectOptional [GridNodeExaminer] getOrElse new SquareNodeExaminer
  
  /* Maps a direction to a neighbor. */
  private var neighbors = Map[MovementDirection, Tile]()

  /** X coordinate for this node. */
  override def x = xpos
  
  /** Sets a new x coordinate. Can only be set once this way. */
  override def x_=(pos: Int) = if (!xSet) { 
    xpos = pos 
    xSet = true
  }
  
  /** Y coordinate for this node. */
  override def y = ypos
  
  /** Sets a new y coordinate. Can only be set once this way. */
  override def y_=(pos: Int) = if (!ySet) { 
    ypos = pos
    ySet = true
  }
  
  /** Returns every node adjacent to this one. */
  override def getNeighbors = neighbors.values.toList
  
  /** Returns every position adjacent to this node. */
  override def getNeighborPositions: List[(Int, Int)] = {
    nodeExaminer.getNeighborPositions(this)
  }
  
  /** Returns an adjacent neighbor in a specified direction. */
  override def getNeighbor(m: MovementDirection): Option[Tile] = {
    for (neighbor <- getNeighbors) {
      getDirection(neighbor) match {
        case Some(moveDir) => if (moveDir == m) return Option(neighbor)
        case None => {}
      }
    }
    
    None
  }
  
  /** Adds a single node neighbor to this one. */
  override def addNeighbor(h: Tile): Boolean = {
    if (!neighbors.values.exists(_ equals h) && (this canConnectTo h)) {
      getDirection(h) match {
        case Some(direction) => neighbors = neighbors + (direction -> h)
        case None => return false
      }
      true
      
    } else {
      false
    }   
  }
  
  /** Removes a single neighbor from this node, if one exists. */
  override def removeNeighbor(h: Tile): Boolean = {
    if (neighbors.values.exists(_ equals h)) {
      neighbors = neighbors.filter(_._2 != h)
      true
      
    } else {
      false
    }
  }
  
  /** Checks if a node is adjacent to this one. */
  override def canConnectTo(h: Tile): Boolean = nodeExaminer.canConnectTo(h, this)
  
  private def getDirection(h: Tile): Option[MovementDirection] = nodeExaminer.getDirection(h, this)
    
  override def toString() = "Node{" + xpos + ", " + ypos + "}"
	
}